namespace hjc {

	export class ViewManager extends Singleton implements ILaunchable {

		private $menus: View[] = [];			//菜单实例
		private $scenes: Scene[] = [];			//场景实例
		private $panels: Panel[] = [];			//面板实例
		private $dialogs: Dialog[] = [];		//对话框实例

		public launch(options: LaunchOptions): ViewManager { return this; }
		public getMenu<M extends View>(clazz: new () => M): M { return this._get(clazz, this.$menus); }
		public getScene<S extends Scene>(clazz: new () => S): S { return this._get(clazz, this.$scenes); }
		public getPanel<P extends Panel>(clazz: new () => P): P { return this._get(clazz, this.$panels); }
		public getDialog<D extends Dialog>(clazz: new () => D): D { return this._get(clazz, this.$dialogs); }

		public enterScene(sceneClass: new () => Scene, ...params: any[]): void {
			if (!!game.curScene) {
				if (game.curScene instanceof sceneClass) return;
				this._quit(game.curScene);
			}
			let scene = this.getScene(sceneClass);
			scene['$lastScene'] = game.curScene;
			game.curScene = scene;
			this._display(scene, ...params);
		}

		public backScene(): void {
			if (!game.curScene) { hjc.warn('no current scene!'); return; }
			if (!game.curScene['$lastScene']) { hjc.warn('this is this last scene.'); return; }
			this._quit(game.curScene);
			this._front(<Scene>game.curScene['$lastScene']);
		}

		public show(viewClass: new () => (Panel | Dialog), ...params: any[]): void {
			if (egret.getQualifiedSuperclassName(viewClass) === egret.getQualifiedClassName(Panel)) this.showPanel(<new () => Panel>viewClass, ...params);
			else this.showDialog(<new () => Dialog>viewClass, ...params);
		}

		public showPanel(panelClass: new () => Panel, ...params: any[]): void {
			if (!!game.topPanel && game.topPanel instanceof panelClass) return;
			let panel = this.getPanel(panelClass);
			let showns = this._shownPanels;
			if (notEmptyArr(showns) && !!panel.configs.exclusive) showns.forEach(shown => { if (shown !== panel) this._quit(shown); });
			else if (!!game.topPanel && !panel.configs.exclusive) this._back(game.topPanel);
			this._display(ui.setTop(panel, game.layer.panel), ...params);
		}

		public hidePanels(): void {
			let panels = this._shownPanels;
			if (notEmptyArr(panels)) panels.forEach(panel => this._quit(panel));
			game.topPanel = null;
		}

		public showDialog(dialogClass: new () => Dialog, ...params: any[]): void {
			if (!!game.topDialog && game.topDialog instanceof dialogClass) return;
			let dialog = this.getDialog(dialogClass);
			let showns = this._shownDialogs;
			if (notEmptyArr(showns) && !!dialog.configs.exclusive) showns.forEach(shown => { if (shown !== dialog) this._quit(shown); });
			else if (!!game.topDialog && !dialog.configs.exclusive) this._back(game.topDialog);
			this._display(ui.setTop(dialog, game.layer.dialog), ...params);
		}

		public hideDialogs(): void {
			let dialogs = this._shownDialogs;
			if (notEmptyArr(dialogs)) dialogs.forEach(dialog => this._quit(dialog));
			game.topDialog = null;
		}

		public hideView(view: Panel | Dialog): void {
			if (view instanceof Panel) {
				let showns = this._shownPanels;
				if (showns.excludes(view)) return;
				this._quit(view);
				if (game.topPanel === view) {
					if (showns.length > 1) this._front(showns.tail());
					else game.topPanel = null;
				}
			} else if (view instanceof Dialog) {
				let showns = this._shownDialogs;
				if (showns.excludes(view)) return;
				this._quit(view);
				if (game.topDialog === view) {
					if (showns.length > 1) this._front(showns.tail());
					else game.topDialog = null;
				}
			}
		}

		public getAll(): View[] { return this.$menus.merge(this.$scenes, this.$panels, this.$dialogs); }

		private _get<V extends View>(clazz: new () => V, views: View[]): V {
			for (let view of views) {
				if (view instanceof clazz && view instanceof Dialog && view.configs.multi) continue;
				if (view instanceof clazz) return view;
			}
			let created = new clazz; views.push(created); return created;
		}
		private get _shownPanels(): Panel[] { return ui.getChildren<Panel>(game.layer.panel, Panel); }
		private get _shownDialogs(): Dialog[] { return ui.getChildren<Dialog>(game.layer.dialog, Dialog); }
		private _quit(view: View): void {
			effect.playHide(view, view.configs.hideMode);
			if (view instanceof Scene && !!view.configs.closeAll) {
				this.hidePanels(); this.hideDialogs();
			} else if (view instanceof Panel || view instanceof Dialog) ui.remove(view.backRect);
			(<Function>view['quit'])();
		}
		private _back(view: Panel | Dialog): void { (<Function>view['back'])(); }
		private _topLayer(view: View): eui.Group {
			if (view instanceof Scene) { game.curScene = view; return game.layer.scene; }
			else if (view instanceof Panel) { game.topPanel = view; return game.layer.panel; }
			else if (view instanceof Dialog) { game.topDialog = view; return game.layer.dialog; }
		}
		private _front(view: View): void {
			effect.playShow(view, view.configs.showMode, this._topLayer(view));
			(<Function>view['front'])();
		}
		private _display(view: View, ...params: any[]): void {
			view['$times']++;
			if (view instanceof Panel && view.backRect && view.backRect.alpha > 0) game.layer.panel.addChild(view.backRect);
			else if (view instanceof Dialog && view.backRect && view.backRect.alpha > 0) game.layer.dialog.addChild(view.backRect);
			effect.playShow(view, view.configs.showMode, this._topLayer(view));
			(<Function>view['displayed'])(...params);
		}
	}
}